// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/pepper_plugin_list.h"

#include <stddef.h>
#include <stdint.h>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pepper_plugin_info.h"
#include "ppapi/shared_impl/ppapi_permissions.h"

namespace content {
namespace {

    // The maximum number of plugins allowed to be registered from command line.
    const size_t kMaxPluginsToRegisterFromCommandLine = 64;

    // Appends any plugins from the command line to the given vector.
    void ComputePluginsFromCommandLine(std::vector<PepperPluginInfo>* plugins)
    {
        // On Linux, once we're sandboxed, we can't know if a plugin is available or
        // not. But (on Linux) this function is always called once before we're
        // sandboxed. So when this function is called for the first time we set a
        // flag if the plugin file is available. Then we can skip the check on file
        // existence in subsequent calls if the flag is set.
        // NOTE: In theory we could have unlimited number of plugins registered in
        // command line. But in practice, 64 plugins should be more than enough.
        static uint64_t skip_file_check_flags = 0;
        static_assert(
            kMaxPluginsToRegisterFromCommandLine <= sizeof(skip_file_check_flags) * 8,
            "max plugins to register from command line exceeds limit");

        bool out_of_process = true;
        if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                switches::kPpapiInProcess))
            out_of_process = false;

        const std::string value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
            switches::kRegisterPepperPlugins);
        if (value.empty())
            return;

        // FORMAT:
        // command-line = <plugin-entry> + *( LWS + "," + LWS + <plugin-entry> )
        // plugin-entry =
        //    <file-path> +
        //    ["#" + <name> + ["#" + <description> + ["#" + <version>]]] +
        //    *1( LWS + ";" + LWS + <mime-type-data> )
        // mime-type-data = <mime-type> + [ LWS + "#" + LWS + <extension> ]
        std::vector<std::string> modules = base::SplitString(
            value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

        size_t plugins_to_register = modules.size();
        if (plugins_to_register > kMaxPluginsToRegisterFromCommandLine) {
            DVLOG(1) << plugins_to_register << " pepper plugins registered from"
                     << " command line which exceeds the limit (maximum "
                     << kMaxPluginsToRegisterFromCommandLine << " plugins allowed)";
            plugins_to_register = kMaxPluginsToRegisterFromCommandLine;
        }

        for (size_t i = 0; i < plugins_to_register; ++i) {
            std::vector<std::string> parts = base::SplitString(
                modules[i], ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
            if (parts.size() < 2) {
                DVLOG(1) << "Required mime-type not found";
                continue;
            }

            std::vector<std::string> name_parts = base::SplitString(
                parts[0], "#", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

            PepperPluginInfo plugin;
            plugin.is_out_of_process = out_of_process;
#if defined(OS_WIN)
            // This means we can't provide plugins from non-ASCII paths, but
            // since this switch is only for development I don't think that's
            // too awful.
            plugin.path = base::FilePath(base::ASCIIToUTF16(name_parts[0]));
#else
            plugin.path = base::FilePath(name_parts[0]);
#endif

            uint64_t index_mask = 1ULL << i;
            if (!(skip_file_check_flags & index_mask)) {
                if (base::PathExists(plugin.path)) {
                    skip_file_check_flags |= index_mask;
                } else {
                    DVLOG(1) << "Plugin doesn't exist: " << plugin.path.MaybeAsASCII();
                    continue;
                }
            }

            if (name_parts.size() > 1)
                plugin.name = name_parts[1];
            if (name_parts.size() > 2)
                plugin.description = name_parts[2];
            if (name_parts.size() > 3)
                plugin.version = name_parts[3];
            for (size_t j = 1; j < parts.size(); ++j) {
                std::vector<std::string> mime_parts = base::SplitString(
                    parts[j], "#", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
                DCHECK_GE(mime_parts.size(), 1u);
                std::string mime_extension;
                if (mime_parts.size() > 1)
                    mime_extension = mime_parts[1];
                WebPluginMimeType mime_type(mime_parts[0],
                    mime_extension,
                    plugin.description);
                plugin.mime_types.push_back(mime_type);
            }

            // If the plugin name is empty, use the filename.
            if (plugin.name.empty()) {
                plugin.name = base::UTF16ToUTF8(plugin.path.BaseName().LossyDisplayName());
            }

            // Command-line plugins get full permissions.
            plugin.permissions = ppapi::PERMISSION_ALL_BITS;

            plugins->push_back(plugin);
        }
    }

} // namespace

bool MakePepperPluginInfo(const WebPluginInfo& webplugin_info,
    PepperPluginInfo* pepper_info)
{
    if (!webplugin_info.is_pepper_plugin())
        return false;

    pepper_info->is_out_of_process = webplugin_info.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;

    pepper_info->path = base::FilePath(webplugin_info.path);
    pepper_info->name = base::UTF16ToASCII(webplugin_info.name);
    pepper_info->description = base::UTF16ToASCII(webplugin_info.desc);
    pepper_info->version = base::UTF16ToASCII(webplugin_info.version);
    pepper_info->mime_types = webplugin_info.mime_types;
    pepper_info->permissions = webplugin_info.pepper_permissions;

    return true;
}

void ComputePepperPluginList(std::vector<PepperPluginInfo>* plugins)
{
    GetContentClient()->AddPepperPlugins(plugins);
    ComputePluginsFromCommandLine(plugins);
}

} // namespace content
